LÄs upp kraften i React Portals för att skapa tillgÀngliga och visuellt tilltalande modaler och tooltips, vilket förbÀttrar anvÀndarupplevelsen och komponentstrukturen.
React Portals: BemÀstra modaler och tooltips för en förbÀttrad anvÀndarupplevelse
Inom modern webbutveckling Àr det av yttersta vikt att skapa intuitiva och engagerande anvÀndargrÀnssnitt. React, ett populÀrt JavaScript-bibliotek för att bygga anvÀndargrÀnssnitt, erbjuder olika verktyg och tekniker för att uppnÄ detta. Ett sÄdant kraftfullt verktyg Àr React Portals. Detta blogginlÀgg fördjupar sig i vÀrlden av React Portals, med fokus pÄ deras tillÀmpning för att bygga tillgÀngliga och visuellt tilltalande modaler och tooltips.
Vad Àr React Portals?
React Portals erbjuder ett sÀtt att rendera en komponents barn till en DOM-nod som existerar utanför förÀldra-komponentens DOM-hierarki. Enkelt uttryckt lÄter det dig bryta dig loss frÄn det vanliga React-komponenttrÀdet och infoga element direkt i en annan del av HTML-strukturen. Detta Àr sÀrskilt anvÀndbart i situationer dÀr du behöver kontrollera stacking context eller positionera element utanför deras förÀldracontainers grÀnser.
Traditionellt sett renderas React-komponenter som barn till sina förÀldrakomponenter inom DOM. Detta kan ibland leda till stil- och layoututmaningar, sÀrskilt nÀr man hanterar element som modaler eller tooltips som behöver visas ovanpÄ annat innehÄll eller positioneras i förhÄllande till visningsomrÄdet. React Portals erbjuder en lösning genom att lÄta dessa element renderas direkt i en annan del av DOM-trÀdet, och dÀrmed kringgÄ dessa begrÀnsningar.
Varför anvÀnda React Portals?
Flera viktiga fördelar gör React Portals till ett vÀrdefullt verktyg i din React-utvecklingsarsenal:
- FörbÀttrad styling och layout: Portaler lÄter dig positionera element utanför deras förÀldracontainer, vilket övervinner stilproblem orsakade av
overflow: hidden,z-index-begrÀnsningar eller komplexa layoutrestriktioner. FörestÀll dig en modal som behöver tÀcka hela skÀrmen, Àven om dess förÀldracontainer haroverflow: hiddeninstÀllt. Portaler lÄter dig rendera modalen direkt ibody, och kringgÄr dÀrmed denna begrÀnsning. - FörbÀttrad tillgÀnglighet: Portaler Àr avgörande för tillgÀnglighet, sÀrskilt nÀr man hanterar modaler. Genom att rendera modalens innehÄll direkt i
bodykan du enkelt hantera focus trapping, vilket sÀkerstÀller att anvÀndare med skÀrmlÀsare eller tangentbordsnavigering stannar kvar i modalen medan den Àr öppen. Detta Àr avgörande för att erbjuda en sömlös och tillgÀnglig anvÀndarupplevelse. - Renare komponentstruktur: Genom att rendera innehÄllet för modaler eller tooltips utanför huvudkomponenttrÀdet kan du hÄlla din komponentstruktur renare och mer hanterbar. Denna uppdelning av ansvarsomrÄden kan göra din kod lÀttare att lÀsa, förstÄ och underhÄlla.
- Undvika problem med stacking context: Stacking contexts i CSS kan vara notoriskt svÄra att hantera. Portaler hjÀlper dig att undvika dessa problem genom att lÄta dig rendera element direkt i roten av DOM, vilket sÀkerstÀller att de alltid Àr korrekt positionerade i förhÄllande till andra element pÄ sidan.
Implementera modaler med React Portals
Modaler Àr ett vanligt UI-mönster som anvÀnds för att visa viktig information eller be anvÀndare om input. LÄt oss utforska hur man skapar en modal med hjÀlp av React Portals.
1. Skapa portalroten
Först mÄste du skapa en DOM-nod dÀr modalen ska renderas. Detta görs vanligtvis genom att lÀgga till ett div-element med ett specifikt ID i din HTML-fil (vanligtvis i body):
<div id="modal-root"></div>
2. Skapa modalkomponenten
Skapa sedan en React-komponent som representerar modalen. Denna komponent kommer att innehÄlla modalens innehÄll och logik.
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
const Modal = ({ isOpen, onClose, children }) => {
const [mounted, setMounted] = useState(false);
const modalRoot = useRef(document.getElementById('modal-root'));
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
if (!isOpen) return null;
const modalContent = (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
<button onClick={onClose}>StÀng</button>
</div>
</div>
);
return mounted && modalRoot.current
? ReactDOM.createPortal(modalContent, modalRoot.current)
: null;
};
export default Modal;
Förklaring:
isOpenprop: BestÀmmer om modalen Àr synlig.onCloseprop: En funktion för att stÀnga modalen.childrenprop: InnehÄllet som ska visas inuti modalen.modalRootref: Refererar till DOM-noden dÀr modalen kommer att renderas (#modal-root).useEffecthook: SÀkerstÀller att modalen endast renderas efter att komponenten har monterats för att undvika problem med att portalroten inte Àr tillgÀnglig omedelbart.ReactDOM.createPortal: Detta Àr nyckeln till att anvÀnda React Portals. Den tar tvÄ argument: React-elementet som ska renderas (modalContent) och DOM-noden dÀr det ska renderas (modalRoot.current).- Klicka pÄ overlayen: StÀnger modalen. Vi anvÀnder
e.stopPropagation()pÄmodal-content-diven för att förhindra att klick inuti modalen stÀnger den.
3. AnvÀnda modalkomponenten
Nu kan du anvÀnda Modal-komponenten i din applikation:
import React, { useState } from 'react';
import Modal from './Modal';
const App = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);
return (
<div>
<button onClick={openModal}>Ăppna modal</button>
<Modal isOpen={isModalOpen} onClose={closeModal}>
<h2>ModalinnehÄll</h2>
<p>Detta Àr innehÄllet i modalen.</p>
</Modal>
</div>
);
};
export default App;
Detta exempel visar hur man kontrollerar modalens synlighet med hjÀlp av isOpen-propen och funktionerna openModal och closeModal. InnehÄllet inom <Modal>-taggarna kommer att renderas inuti modalen.
4. Styla modalen
LÀgg till CSS-stilar för att positionera och styla modalen. HÀr Àr ett grundlÀggande exempel:
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5); /* Halvtransparent bakgrund */
display: flex;
justify-content: center;
align-items: center;
z-index: 1000; /* SÀkerstÀll att den ligger ovanpÄ annat innehÄll */
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
Förklaring av CSS:
position: fixed: SÀkerstÀller att modalen tÀcker hela visningsomrÄdet, oavsett scrollning.background-color: rgba(0, 0, 0, 0.5): Skapar en halvtransparent overlay bakom modalen.display: flex, justify-content: center, align-items: center: Centrerar modalen horisontellt och vertikalt.z-index: 1000: SÀkerstÀller att modalen renderas ovanpÄ alla andra element pÄ sidan.
5. TillgÀnglighetsaspekter för modaler
TillgÀnglighet Àr avgörande nÀr man implementerar modaler. HÀr Àr nÄgra viktiga övervÀganden:
- Fokushantering: NÀr modalen öppnas ska fokus automatiskt flyttas till ett element inuti modalen (t.ex. det första inmatningsfÀltet eller en stÀngningsknapp). NÀr modalen stÀngs ska fokus ÄtergÄ till elementet som utlöste modalens öppning. Detta uppnÄs ofta med hjÀlp av Reacts
useRef-hook för att lagra det tidigare fokuserade elementet. - Tangentbordsnavigering: SÀkerstÀll att anvÀndare kan navigera i modalen med tangentbordet (Tab-tangenten). Fokus bör vara fÄngat inuti modalen, vilket förhindrar anvÀndare frÄn att oavsiktligt tabba ut ur den. Bibliotek som
react-focus-lockkan hjÀlpa till med detta. - ARIA-attribut: AnvÀnd ARIA-attribut för att ge semantisk information om modalen till skÀrmlÀsare. AnvÀnd till exempel
aria-modal="true"pÄ modalcontainern ocharia-labelelleraria-labelledbyför att ge en beskrivande etikett för modalen. - StÀngningsmekanism: TillhandahÄll flera sÀtt att stÀnga modalen, som en stÀngningsknapp, att klicka pÄ overlayen eller att trycka pÄ Escape-tangenten.
Exempel pÄ fokushantering (med useRef):
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
const Modal = ({ isOpen, onClose, children }) => {
const [mounted, setMounted] = useState(false);
const modalRoot = useRef(document.getElementById('modal-root'));
const firstFocusableElement = useRef(null);
const previouslyFocusedElement = useRef(null);
useEffect(() => {
setMounted(true);
if (isOpen) {
previouslyFocusedElement.current = document.activeElement;
if (firstFocusableElement.current) {
firstFocusableElement.current.focus();
}
const handleKeyDown = (event) => {
if (event.key === 'Escape') {
onClose();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
if (previouslyFocusedElement.current) {
previouslyFocusedElement.current.focus();
}
};
}
return () => setMounted(false);
}, [isOpen, onClose]);
if (!isOpen) return null;
const modalContent = (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<h2>ModalinnehÄll</h2>
<p>Detta Àr innehÄllet i modalen.</p>
<input type="text" ref={firstFocusableElement} /> <!-- Första fokuserbara elementet -->
<button onClick={onClose}>StÀng</button>
</div>
</div>
);
return mounted && modalRoot.current
? ReactDOM.createPortal(modalContent, modalRoot.current)
: null;
};
export default Modal;
Förklaring av koden för fokushantering:
previouslyFocusedElement.current: Lagrar elementet som hade fokus innan modalen öppnades.firstFocusableElement.current: Refererar till det första fokuserbara elementet *inuti* modalen (i detta exempel, ett textinmatningsfÀlt).- NÀr modalen öppnas (
isOpenÀr true):- Det för nÀrvarande fokuserade elementet lagras.
- Fokus flyttas till
firstFocusableElement.current. - En hÀndelselyssnare lÀggs till för att lyssna efter Escape-tangenten, vilket stÀnger modalen.
- NÀr modalen stÀngs (uppstÀdningsfunktion):
- HÀndelselyssnaren för Escape-tangenten tas bort.
- Fokus ÄtergÄr till elementet som tidigare var fokuserat.
Implementera tooltips med React Portals
Tooltips Àr smÄ, informativa popups som visas nÀr en anvÀndare för muspekaren över ett element. React Portals kan anvÀndas för att skapa tooltips som Àr korrekt positionerade, oavsett förÀldraelementets styling eller layout.
1. Skapa portalroten (om den inte redan Àr skapad)
Om du inte redan har skapat en portalrot för modaler, lÀgg till ett div-element med ett specifikt ID i din HTML-fil (vanligtvis i body):
<div id="tooltip-root"></div>
2. Skapa tooltip-komponenten
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
const Tooltip = ({ text, children, position = 'top' }) => {
const [isVisible, setIsVisible] = useState(false);
const [positionStyle, setPositionStyle] = useState({});
const [mounted, setMounted] = useState(false);
const tooltipRoot = useRef(document.getElementById('tooltip-root'));
const tooltipRef = useRef(null);
const triggerRef = useRef(null);
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
const handleMouseEnter = () => {
setIsVisible(true);
updatePosition();
};
const handleMouseLeave = () => {
setIsVisible(false);
};
const updatePosition = () => {
if (!triggerRef.current || !tooltipRef.current) return;
const triggerRect = triggerRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
let top = 0;
let left = 0;
switch (position) {
case 'top':
top = triggerRect.top - tooltipRect.height - 5; // 5px spacing
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
break;
case 'bottom':
top = triggerRect.bottom + 5;
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
break;
case 'left':
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
left = triggerRect.left - tooltipRect.width - 5;
break;
case 'right':
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
left = triggerRect.right + 5;
break;
default:
break;
}
setPositionStyle({
top: `${top}px`,
left: `${left}px`,
});
};
const tooltipContent = isVisible && (
<div className="tooltip" style={positionStyle} ref={tooltipRef}>
{text}
</div>
);
return (
<span
ref={triggerRef}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{children}
{mounted && tooltipRoot.current ? ReactDOM.createPortal(tooltipContent, tooltipRoot.current) : null}
</span>
);
};
export default Tooltip;
Förklaring:
textprop: Texten som ska visas i tooltipen.childrenprop: Elementet som utlöser tooltipen (det element som anvÀndaren för muspekaren över).positionprop: Positionen för tooltipen i förhÄllande till utlösarelementet ('top', 'bottom', 'left', 'right'). StandardvÀrdet Àr 'top'.isVisiblestate: Kontrollerar synligheten för tooltipen.tooltipRootref: Refererar till DOM-noden dÀr tooltipen kommer att renderas (#tooltip-root).tooltipRefref: Refererar till sjÀlva tooltip-elementet, anvÀnds för att berÀkna dess dimensioner.triggerRefref: Refererar till elementet som utlöser tooltipen (children).handleMouseEnterochhandleMouseLeave: HÀndelsehanterare för nÀr muspekaren förs över utlösarelementet.updatePosition: BerÀknar den korrekta positionen för tooltipen baserat pÄposition-propen och dimensionerna för utlösar- och tooltip-elementen. Den anvÀndergetBoundingClientRect()för att hÀmta position och dimensioner för elementen i förhÄllande till visningsomrÄdet.ReactDOM.createPortal: Renderar tooltip-innehÄllet itooltipRoot.
3. AnvÀnda tooltip-komponenten
import React from 'react';
import Tooltip from './Tooltip';
const App = () => {
return (
<div>
<p>
HÄll muspekaren över denna <Tooltip text="Detta Àr en tooltip!\nMed flera rader." position="bottom">text</Tooltip> för att se en tooltip.
</p>
<button>
HÄll muspekaren <Tooltip text="Knapp-tooltip" position="top">hÀr</Tooltip> för en tooltip.
</button>
</div>
);
};
export default App;
Detta exempel visar hur man anvÀnder Tooltip-komponenten för att lÀgga till tooltips till text och knappar. Du kan anpassa tooltipens text och position med hjÀlp av text- och position-proparna.
4. Styla tooltipen
LÀgg till CSS-stilar för att positionera och styla tooltipen. HÀr Àr ett grundlÀggande exempel:
.tooltip {
position: absolute;
background-color: rgba(0, 0, 0, 0.8); /* Mörk bakgrund */
color: white;
padding: 5px;
border-radius: 3px;
font-size: 12px;
z-index: 1000; /* SÀkerstÀll att den ligger ovanpÄ annat innehÄll */
white-space: pre-line; /* Respektera radbrytningar i text-propen */
}
Förklaring av CSS:
position: absolute: Positionerar tooltipen i förhÄllande tilltooltip-root. FunktionenupdatePositioni React-komponenten berÀknar de exaktatop- ochleft-vÀrdena för att positionera tooltipen nÀra utlösarelementet.background-color: rgba(0, 0, 0, 0.8): Skapar en lÀtt transparent mörk bakgrund för tooltipen.white-space: pre-line: Detta Àr viktigt för att bevara radbrytningar som du kan inkludera itext-propen. Utan detta skulle all tooltip-text visas pÄ en enda rad.
Globala övervÀganden och bÀsta praxis
NÀr du utvecklar React-applikationer för en global publik, övervÀg dessa bÀsta praxis:
- Internationalisering (i18n): AnvÀnd ett bibliotek som
react-i18nextellerFormatJSför att hantera översÀttningar och lokalisering. Detta gör att du enkelt kan anpassa din applikation till olika sprÄk och regioner. För modaler och tooltips, se till att textinnehÄllet Àr korrekt översatt. - Höger-till-vÀnster (RTL) stöd: För sprÄk som lÀses frÄn höger till vÀnster (t.ex. arabiska, hebreiska), se till att dina modaler och tooltips visas korrekt. Du kan behöva justera positionering och styling av element för att anpassa dem till RTL-layouter. CSS logiska egenskaper (t.ex.
margin-inline-startistÀllet förmargin-left) kan vara till hjÀlp. - Kulturell kÀnslighet: Var medveten om kulturella skillnader nÀr du designar dina modaler och tooltips. Undvik att anvÀnda bilder eller symboler som kan vara stötande eller olÀmpliga i vissa kulturer.
- Tidszoner och datumformat: Om dina modaler eller tooltips visar datum eller tider, se till att de Àr formaterade enligt anvÀndarens lokala instÀllningar och tidszon. Bibliotek som
moment.js(Àven om det Àr Àldre, fortfarande flitigt anvÀnt) ellerdate-fnskan hjÀlpa till med detta. - TillgÀnglighet för olika förmÄgor: Följ tillgÀnglighetsriktlinjer (WCAG) för att sÀkerstÀlla att dina modaler och tooltips Àr anvÀndbara för personer med funktionsnedsÀttningar. Detta inkluderar att tillhandahÄlla alternativ text för bilder, sÀkerstÀlla tillrÀcklig fÀrgkontrast och ge stöd för tangentbordsnavigering.
Slutsats
React Portals Àr ett kraftfullt verktyg för att bygga flexibla och tillgÀngliga anvÀndargrÀnssnitt. Genom att förstÄ hur man anvÀnder dem effektivt kan du skapa modaler och tooltips som förbÀttrar anvÀndarupplevelsen och förbÀttrar strukturen och underhÄllbarheten i dina React-applikationer. Kom ihÄg att prioritera tillgÀnglighet och globala övervÀganden nÀr du utvecklar för en mÄngfaldig publik, för att sÀkerstÀlla att dina applikationer Àr inkluderande och anvÀndbara för alla.